import { Injectable } from '@nestjs/common';
import * as bcrypt from 'bcryptjs';
import * as crypto from 'crypto';
import * as CryptoJS from 'crypto-js';

/**
 * 加密工具类
 * 对应 Java: PasswordEncipher
 */
@Injectable()
export class CryptoUtil {
  /**
   * 生成随机盐值
   */
  static generateSalt(rounds: number = 10): string {
    return bcrypt.genSaltSync(rounds);
  }

  /**
   * 密码加密
   */
  static hashPassword(password: string, salt?: string): string {
    if (salt) {
      return bcrypt.hashSync(password, salt);
    }
    return bcrypt.hashSync(password, this.generateSalt());
  }

  /**
   * 密码验证
   */
  static verifyPassword(password: string, hashedPassword: string): boolean {
    return bcrypt.compareSync(password, hashedPassword);
  }

  /**
   * MD5 加密
   */
  static md5(text: string): string {
    return crypto.createHash('md5').update(text).digest('hex');
  }

  /**
   * SHA1 加密
   */
  static sha1(text: string): string {
    return crypto.createHash('sha1').update(text).digest('hex');
  }

  /**
   * SHA256 加密
   */
  static sha256(text: string): string {
    return crypto.createHash('sha256').update(text).digest('hex');
  }

  /**
   * SHA512 加密
   */
  static sha512(text: string): string {
    return crypto.createHash('sha512').update(text).digest('hex');
  }

  /**
   * 生成随机字符串
   */
  static randomString(length: number = 32): string {
    return crypto.randomBytes(length).toString('hex');
  }

  /**
   * AES 加密
   */
  static aesEncrypt(text: string, key: string): string {
    return CryptoJS.AES.encrypt(text, key).toString();
  }

  /**
   * AES 解密
   */
  static aesDecrypt(encryptedText: string, key: string): string {
    const bytes = CryptoJS.AES.decrypt(encryptedText, key);
    return bytes.toString(CryptoJS.enc.Utf8);
  }

  /**
   * DES 加密
   */
  static desEncrypt(text: string, key: string): string {
    return CryptoJS.DES.encrypt(text, key).toString();
  }

  /**
   * DES 解密
   */
  static desDecrypt(encryptedText: string, key: string): string {
    const bytes = CryptoJS.DES.decrypt(encryptedText, key);
    return bytes.toString(CryptoJS.enc.Utf8);
  }

  /**
   * Base64 编码
   */
  static base64Encode(text: string): string {
    return Buffer.from(text, 'utf8').toString('base64');
  }

  /**
   * Base64 解码
   */
  static base64Decode(encodedText: string): string {
    return Buffer.from(encodedText, 'base64').toString('utf8');
  }

  /**
   * URL 安全的 Base64 编码
   */
  static base64UrlEncode(text: string): string {
    return Buffer.from(text, 'utf8')
      .toString('base64')
      .replace(/\+/g, '-')
      .replace(/\//g, '_')
      .replace(/=/g, '');
  }

  /**
   * URL 安全的 Base64 解码
   */
  static base64UrlDecode(encodedText: string): string {
    // 补齐填充字符
    const padded = encodedText + '='.repeat((4 - (encodedText.length % 4)) % 4);
    const base64 = padded.replace(/-/g, '+').replace(/_/g, '/');
    return Buffer.from(base64, 'base64').toString('utf8');
  }

  /**
   * 生成 HMAC
   */
  static hmac(text: string, key: string, algorithm: string = 'sha256'): string {
    return crypto.createHmac(algorithm, key).update(text).digest('hex');
  }

  /**
   * 生成 JWT Token
   */
  static generateJwtToken(
    payload: any,
    secret: string,
    expiresIn: string = '1h',
  ): string {
    const header = {
      alg: 'HS256',
      typ: 'JWT',
    };

    const now = Math.floor(Date.now() / 1000);
    const exp = now + this.parseExpiresIn(expiresIn);

    const encodedHeader = this.base64UrlEncode(JSON.stringify(header));
    const encodedPayload = this.base64UrlEncode(
      JSON.stringify({ ...payload, exp }),
    );
    const signature = this.hmac(`${encodedHeader}.${encodedPayload}`, secret);

    return `${encodedHeader}.${encodedPayload}.${signature}`;
  }

  /**
   * 验证 JWT Token
   */
  static verifyJwtToken(token: string, secret: string): any {
    const parts = token.split('.');
    if (parts.length !== 3) {
      throw new Error('Invalid token format');
    }

    const [header, payload, signature] = parts;
    const expectedSignature = this.hmac(`${header}.${payload}`, secret);

    if (signature !== expectedSignature) {
      throw new Error('Invalid token signature');
    }

    const decodedPayload = JSON.parse(this.base64UrlDecode(payload));
    const now = Math.floor(Date.now() / 1000);

    if (decodedPayload.exp && decodedPayload.exp < now) {
      throw new Error('Token expired');
    }

    return decodedPayload;
  }

  /**
   * 解析过期时间
   */
  private static parseExpiresIn(expiresIn: string): number {
    const unit = expiresIn.slice(-1);
    const value = parseInt(expiresIn.slice(0, -1));

    switch (unit) {
      case 's':
        return value;
      case 'm':
        return value * 60;
      case 'h':
        return value * 60 * 60;
      case 'd':
        return value * 60 * 60 * 24;
      default:
        return 3600; // 默认1小时
    }
  }

  /**
   * 生成 API 签名
   */
  static generateApiSignature(
    params: Record<string, any>,
    secret: string,
  ): string {
    // 按参数名排序
    const sortedParams = Object.keys(params)
      .sort()
      .map((key) => `${key}=${params[key]}`)
      .join('&');

    return this.hmac(sortedParams, secret);
  }

  /**
   * 验证 API 签名
   */
  static verifyApiSignature(
    params: Record<string, any>,
    signature: string,
    secret: string,
  ): boolean {
    const expectedSignature = this.generateApiSignature(params, secret);
    return signature === expectedSignature;
  }

  /**
   * 生成文件哈希
   */
  static generateFileHash(
    buffer: Buffer,
    algorithm: string = 'sha256',
  ): string {
    return crypto.createHash(algorithm).update(buffer).digest('hex');
  }

  /**
   * 生成密码强度
   */
  static getPasswordStrength(password: string): {
    score: number;
    level: 'weak' | 'medium' | 'strong' | 'very-strong';
    suggestions: string[];
  } {
    let score = 0;
    const suggestions: string[] = [];

    // 长度检查
    if (password.length >= 8) {
      score += 1;
    } else {
      suggestions.push('密码长度至少8位');
    }

    // 包含小写字母
    if (/[a-z]/.test(password)) {
      score += 1;
    } else {
      suggestions.push('包含小写字母');
    }

    // 包含大写字母
    if (/[A-Z]/.test(password)) {
      score += 1;
    } else {
      suggestions.push('包含大写字母');
    }

    // 包含数字
    if (/\d/.test(password)) {
      score += 1;
    } else {
      suggestions.push('包含数字');
    }

    // 包含特殊字符
    if (/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
      score += 1;
    } else {
      suggestions.push('包含特殊字符');
    }

    // 长度超过12位
    if (password.length >= 12) {
      score += 1;
    }

    let level: 'weak' | 'medium' | 'strong' | 'very-strong';
    if (score <= 2) {
      level = 'weak';
    } else if (score <= 3) {
      level = 'medium';
    } else if (score <= 4) {
      level = 'strong';
    } else {
      level = 'very-strong';
    }

    return { score, level, suggestions };
  }
}
